// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using SnapObjects.Commons;

namespace SnapObjects.Data
{
    internal class XmlTemplateImportDataParser<TModel> : ITemplateImportDataParser
    {
        private TemplateImportSchema _schema;

        private ModelEntry<TModel> _entry;

        private List<ModelEntry<TModel>> _entries;

        public IEnumerable<IModelEntry> Parse(string data, TemplateImportSchema schema)
        {
            _schema = schema;

            _entries = new List<ModelEntry<TModel>>();

            _entry = new ModelEntry<TModel>(ModelState.NewModified);

            var document = XDocument.Parse(data);

            this.Parse(document.Root);

            if (_entry.Properties.Length != 0)
            {
                _entries.Add(_entry);
            }

            return _entries;
        }

        private void Parse(XElement element, TemplateImportDataNode parent = null)
        {
            var node = new TemplateImportDataNode
            {
                NodeName = element.Name.LocalName,
                Parent = parent
            };

            foreach (var item in element.Elements().OrderBy(x => x.HasElements))
            {
                if (item.HasElements)
                {
                    var data = node.Childs.FirstOrDefault(x => x.NodeName.Equals(item.Name.LocalName));

                    if (data != null && _entry.Properties.Length != 0)
                    {
                        _entries.Add(_entry);

                        _entry = new ModelEntry<TModel>(ModelState.NewModified);

                        this.SetGroupItem(node);
                    }

                    this.Parse(item, node);

                    var nodeItem = new TemplateImportDataNodeItem
                    {
                        NodeName = item.Name.LocalName,
                        NodeType = DataNodeType.Object,
                    };

                    node.Childs.Add(nodeItem);
                }
                else if (_schema.Nodes.ContainsKey(item.Name.LocalName))
                {
                    var column = _schema.Nodes[item.Name.LocalName];

                    object value;
                    
                    if (!item.IsEmpty && 
                        (!String.IsNullOrWhiteSpace(item.Value) || column.ColumnType == typeof(string)))
                    {
                        try
                        {
                            value = Utils.ChangeType(item.Value, column.ColumnType);
                        }
                        catch
                        {
                            throw new Exception(String.Format(
                                Properties.Resource.TEMPLATEEXPORT_VALUECONVERT_EXCEPTION,
                                item.Value,
                                column.ColumnName,
                                column.ColumnType));
                        }
                    }
                    else
                    {
                        value = column.ColumnType.DefaultValue();
                    }

                    _entry.SetPropertyEntry(column.ColumnName, value, value, PropertyState.Modified);

                    var nodeItem = new TemplateImportDataNodeItem
                    {
                        NodeName = column.ColumnName,
                        NodeValue = value,
                        NodeType = DataNodeType.Value,
                    };

                    node.Childs.Add(nodeItem);
                }
            }
        }

        private void SetGroupItem(TemplateImportDataNode node)
        {
            do
            {
                foreach (var item in node.Childs.Where(x => x.NodeType == DataNodeType.Value))
                {
                    _entry.SetPropertyEntry
                    (
                        item.NodeName, item.NodeValue, item.NodeValue, PropertyState.Modified
                    );
                }

                node = node.Parent;

            } while (node != null);
        }
    }
}
